#pragma once

#include "sqlite3.h"

using namespace std;
using namespace Platform;

namespace SQLite 
{
	ref class Database; 
	public ref class Statement sealed {
	private:
		sqlite3_stmt* stmt;
		Platform::String^ _command;

	private:
		void finalize_safely()
		{
			if(stmt == NULL)
				return;
			try
			{
				sqlite3_finalize(stmt);
			}
			catch(...)
			{
			}
		}

	public:
		property bool Valid;
		Statement(Database^ database, Platform::String^ cmd);

		virtual ~Statement() 
		{
			finalize_safely();
		}

		void Execute() 
		{
			int rc = sqlite3_step(stmt);
			if(rc != SQLITE_DONE)
			{
				throw Platform::Exception::CreateException(rc, "Failed to execute statement");
			}
		}

		bool HasMore() 
		{
			int rc = sqlite3_step(stmt);
			return rc == SQLITE_ROW;
		}

		Platform::String^ ColumnAsTextAt(int index) 
		{
			return ref new Platform::String((const wchar_t*) sqlite3_column_text16(stmt, index));
		}

		int ColumnAsIntAt(int index) 
		{
			return sqlite3_column_int(stmt, index);
		}


		int64 ColumnAsLongAt(int index) 
		{
			return sqlite3_column_int64(stmt, index);
		}

		double ColumnAsDoubleAt(int index) 
		{
			return sqlite3_column_double(stmt, index);
		}

		void BindText(int index, Platform::String^ param) 
		{
			int rc = sqlite3_bind_text16(stmt, index, param->Data(), -1, SQLITE_STATIC);
			if(rc != SQLITE_OK)
			{
				throw Platform::Exception::CreateException(rc, "Failed to bind to column");
			}
		}

		void BindInt(int index, int param) 
		{
			int rc = sqlite3_bind_int(stmt, index, param);
			if(rc != SQLITE_OK)
			{
				throw Platform::Exception::CreateException(rc, "Failed to bind to column");
			}
		}

		void BindLong(int index, int64 param)
		{
			int rc = sqlite3_bind_int64(stmt, index, param);
			if(rc != SQLITE_OK)
			{
				throw Platform::Exception::CreateException(rc, "Failed to bind to column");
			}
		}

		void BindDouble(int index, double param) 
		{
			int rc = sqlite3_bind_double(stmt, index, param);
			if(rc != SQLITE_OK)
			{
				throw Platform::Exception::CreateException(rc, "Failed to bind to column");
			}
		}

		void Reset()
		{
			int rc = sqlite3_reset(stmt);
			if(rc != SQLITE_OK)
			{
				throw Platform::Exception::CreateException(rc, "Failed to reset statement");
			}
		};

		property Platform::String^ Command
		{
			Platform::String^ get () 
			{
				return _command;
			}
		}

	};
}